Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement i18n package #613

Merged
merged 33 commits into from
Aug 17, 2024
Merged

implement i18n package #613

merged 33 commits into from
Aug 17, 2024

Conversation

Jonghakseo
Copy link
Owner

@Jonghakseo Jonghakseo commented Jul 27, 2024

* Please fill in the required items.

Priority*

  • High: This PR needs to be merged first for other tasks.
  • Middle: This PR should be merged quickly to prevent conflicts due to common changes. (default)
  • Low: This PR does not affect other tasks, so it can be merged later.

Purpose of the PR*

Add I18n Package for internationalize in chrome extension

Changes*

Add I18n Package

The i18n package has two main functions.

First, it offers the t(translate) function, ensuring strong type safety and ease of development for managing multiple languages.

The t function behaves exactly the same as chrome.i18n.getMessage in production. However, it is implemented as a dev-only function to allow developers to see quick results without reloading the extension.

스크린샷 2024-07-28 오전 1 18 10

This ensures that features like HMR work as expected while also providing strong type safety by inferring the key values of the messages entered in message.json.

Secondly, developers do not need to manually update the utility functions for type inference whenever adding or removing languages. Through the generate-i18n.mjs file, simply creating or deleting folders and new JSON files is sufficient to prepare support for new languages.

Add Usage into NewTab.tsx

const NewTab = () => {
  //...
  console.log(t('hello', 'World')); // Hello, World ('World' is a substitution)

  return (
    <div>
      <header>
        {/*...*/}
        <Button onClick={exampleThemeStorage.toggle} theme={theme}>
          {t('toggleTheme')} // Toggle Theme
        </Button>
      </header>
    </div>
  );
};

How to check the feature

2024-07-27.11.28.03.mov

Reference

https://developer.chrome.com/docs/extensions/reference/api/i18n
https://developer.chrome.com/docs/extensions/how-to/ui/localization-message-formats#placeholders

Created `initializeWSS` function to encapsulate WebSocket setup. Modified watch-rebuild-plugin to use this new function, promoting code reuse and simplifying maintenance.
Moved HMR code extraction logic to a new `getTranspiledHMRCode` module. This improves code organization and simplifies the `watch-rebuild-plugin.ts` file by removing file reading and path resolution logic.
Integrated multilingual support for the Chrome extension by adding an i18n package with locale files for English and Korean. Implemented translation functions for both development and production environments. Updated `turbo.json` to include i18n build outputs and added necessary configurations and scripts.
Integrate i18n library to handle translations in the NewTab component. Updated the package dependencies and made necessary code changes to utilize i18n for text rendering.
@Jonghakseo Jonghakseo self-assigned this Jul 27, 2024
Jonghakseo and others added 11 commits July 27, 2024 21:48
Added detailed usage instructions and installation guide to README. Refactored the i18n translation functions to streamline message key substitutions, removing unnecessary options and simplifying the API.
Added a caution note to use placeholders with spaces in messages. Adjusted example code in `messages.json` to reflect the correct placeholder format.
A minor correction was made to the example code in the README.md for accurate representation of the formatted output. This change ensures that the spacing in the console.log output aligns with expected results.
Renamed i18n_dev.ts to i18n-dev.ts and i18n_prod.ts to i18n-prod.ts. Updated import statements to reflect the new file names. This change ensures consistency and improves readability in the codebase.
Renamed the import alias `mock` to `t_dev_or_prod` for the i18n module. This change ensures a clearer and more accurate representation of the import's purpose in both development and production environments.
Introduce scripts to auto-generate locale message imports and types. This improves maintainability by ensuring consistency across locale handling and reduces manual intervention by automatically generating necessary files from existing locales.
Add script to automate the i18n file generation. Modify build and ready scripts to include i18n generation step, ensuring the latest localization data is always included.
Removed excessive punctuation from the "toggleTheme" message. This improves readability and aligns with other translations.
@Jonghakseo Jonghakseo added enhancement New feature or request in-progress labels Jul 27, 2024
Introduce documentation on how to add or delete languages in the i18n package. Simplify type checking logic in generate-i18n.mjs to ensure language keys are correctly validated.
Deleted the redundant `messages.json` file from the `en` locale folder in the Chrome extension. This cleanup helps in maintaining a more streamlined codebase by removing unnecessary files.
Moved esbuild configurations and logic into a shared build function in build.mjs. Created build.prod.mjs and simplified build.dev.mjs to use the new build function.
Removed .tsx files from the build entry points to avoid unnecessary bundling of React components. This ensures the build process only includes TypeScript files under the ./lib directory.
Renamed the `genenrate-i8n.mjs` file to `genenrate-i18n.mjs`. Updated the script in `package.json` to reflect this change, ensuring consistency and correct execution.
@Jonghakseo Jonghakseo marked this pull request as ready for review July 27, 2024 15:59
@Jonghakseo Jonghakseo linked an issue Jul 27, 2024 that may be closed by this pull request
Refactor default locale logic to check for exact and region-less matches from supported locales. This enhances accuracy and fallbacks by considering predefined locales before defaulting to the first available locale.
Replace double quotes with single quotes in locale JSON string and ensure consistent single quotes in locale handling. This resolves potential issues with locale detection in different environments.
Reorder the "message" and "description" fields for consistency in both English and Korean locale files. This change improves readability and maintainability of the translation files.
packages/i18n/README.md Outdated Show resolved Hide resolved
packages/i18n/README.md Show resolved Hide resolved
packages/i18n/build.mjs Show resolved Hide resolved
packages/i18n/build.mjs Show resolved Hide resolved
packages/i18n/index.ts Show resolved Hide resolved
packages/i18n/genenrate-i18n.mjs Show resolved Hide resolved
@Jonghakseo Jonghakseo mentioned this pull request Jul 27, 2024
3 tasks
Copy link
Collaborator

@PatrykKuniczak PatrykKuniczak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. pnpm script for generating, should be located in the root(I think good idea is create a pointing script, which run this nested(from i18n) script in root), like other scripts, because until now we was using only root package.json and magic happened in the subfolders :)
    I think this should work on the same way.
  2. There's no function to change language in real time, i mean by user(switching between languages).
  3. Move all 'build.' and 'generate ...' file into 'builders' folder(maybe it should be inside lib?)

After all it's amazing, because it works in all pages, if you want to implement it without turborepo/modules pattern, it's tricky to work properly in background because i was implementing it on legacy version on my project ;)


export function getMessageFromLocale(locale: string) {
switch (locale) {
${locales.map((locale) => ` case '${locale}':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
${locales.map((locale) => ` case '${locale}':
const {message} = await import('../locales/${locale}/messages.json').join("\n")
if (message) {
return message
}else {
throw new Error('Unsupported locale');
}

That's pseudocode, what do you think about this?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import

I think it's better to use dynamic import for that because of so many languages we have.
I know it's build file, but anyway, it could be smaller, each language is next switch case.
With my approach it's almost one liner.
If sb want to use 30 languages, have 30 cases and 30 imports.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we use dynamic import, we have the constraint that the function must be called asynchronously.

However, the problem you suggest is reasonable and I'll have to think about it.

I'm currently maintaining a product in my company that supports 15 different languages, and each language file is about 500kB in size, so obviously the issue of optimizing for multiple languages and dynamic importing becomes very important at this scale.

But what if it's a Chrome extension, not a webpage, and the issue only occurs during the development phase...?
I guess it doesn't really matter. 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends of the extension complexity, as you say.
This solution is good, but if you have better idea, try to implement it, sth more generic.

packages/i18n/README.md Outdated Show resolved Hide resolved
packages/i18n/README.md Outdated Show resolved Hide resolved
}

export const t = (...args: Parameters<typeof translate>) => {
return removePlaceholder(translate(...args));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looking for that, and my thoughts about it have destroyed xD

I think substitusion is placeholders.

PLS create more examples and explanation in README 🔥


const NewTab = () => {
const theme = useStorageSuspense(exampleThemeStorage);
const isLight = theme === 'light';
const logo = isLight ? 'new-tab/logo_horizontal.svg' : 'new-tab/logo_horizontal_dark.svg';

console.log(t('hello', 'World'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OO there's some example, but as i type above, this should be also in README 😸

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I documented it well enough in the README.md in the i18n package, but what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, if you implement 'placeholder' filed in json, there's no need to create more clarification, but no it's a little tricky to use it 😆

@Jonghakseo
Copy link
Owner Author

  1. pnpm script for generating, should be located in the root(I think good idea is create a pointing script, which run this nested(from i18n) script in root), like other scripts, because until now we was using only root package.json and magic happened in the subfolders :)
    I think this should work on the same way.
  2. There's no function to change language in real time, i mean by user(switching between languages).
  3. Move all 'build.' and 'generate ...' file into 'builders' folder(maybe it should be inside lib?)
  1. I agree :)
  2. The ability to change the language in real-time is not something that the Chrome extension's multilingual capabilities provide. There are already many great multilingual libraries that provide the ability to change the language at runtime. What we're focusing on here is a bit of support for how multilingualism is handled in Chrome extensions.
  3. OK :)

After all it's amazing, because it works in all pages, if you want to implement it without turborepo/modules pattern, it's tricky to work properly in background because i was implementing it on legacy version on my project ;)

I didn't test the behavior in the background! I'll check the behavior, and it's true that it does rely on the behavior of Turborepo (more precisely on the watch function).

I thought it would be a good way to do it with less effort :)

@PatrykKuniczak
Copy link
Collaborator

@Jonghakseo It's working good in background in this version :)

  1. Yeah, that's true, but i think it could be good feature, because users don't need to download other package only for switching language.

I was implementing language switching on my project some times ago.
https://github.com/PatrykKuniczak/YT_Notifier/blob/main/react-app/utils/hooks/use-language-switch.ts
https://github.com/PatrykKuniczak/YT_Notifier/blob/main/react-app/src/pages/popup/components/language-selector.tsx

I think if it will be build in, this could be good to our users, let's think about it a little 😸

@Jonghakseo
Copy link
Owner Author

  1. pnpm script for generating, should be located in the root(I think good idea is create a pointing script, which run this nested(from i18n) script in root), like other scripts, because until now we was using only root package.json and magic happened in the subfolders :)
    I think this should work on the same way.
  2. There's no function to change language in real time, i mean by user(switching between languages).
  3. Move all 'build.' and 'generate ...' file into 'builders' folder(maybe it should be inside lib?)
  1. I agree :)
  2. The ability to change the language in real-time is not something that the Chrome extension's multilingual capabilities provide. There are already many great multilingual libraries that provide the ability to change the language at runtime. What we're focusing on here is a bit of support for how multilingualism is handled in Chrome extensions.
  3. OK :)

After all it's amazing, because it works in all pages, if you want to implement it without turborepo/modules pattern, it's tricky to work properly in background because i was implementing it on legacy version on my project ;)

I didn't test the behavior in the background! I'll check the behavior, and it's true that it does rely on the behavior of Turborepo (more precisely on the watch function).

I thought it would be a good way to do it with less effort :)

I've been thinking about it again,

  1. In my user experience, the only thing that will run commands related to the i18n package in the top-level path is `genenrate-i8n', which is already included in the build and ready commands, so I don't think there is any need to run it separately in the top-level path! 😄

  2. There are a lot of things to think about for implementation... First of all, the multilingual capabilities of the Chrome extension are determined by the browser's language setting. If I change the language I want to use at runtime, I have to change to an implementation that doesn't rely on chrome.i18n API. Does that defeat the purpose of providing the convenience of using the chrome.i18n API? I'm not sure. 🫠

  3. I've tried moving the build.~ and generate.~ files to builders or into libs, but it feels like unnecessary encapsulation that's hard to understand.

@PatrykKuniczak
Copy link
Collaborator

@Jonghakseo

  1. Yeah, right
  2. Yeaaah, that's true, i was thinking about users which want to change language for e.g 'english', but then they can change language for entire browser, and respectly plugin also have english. That's a point 😄
  3. Ok, if it's complicating, that's ok to leave it in that shape.

@PatrykKuniczak
Copy link
Collaborator

@Jonghakseo Now users can define 'placeholder' prop in json?
Because i don't see any new examples.

Jonghakseo and others added 3 commits August 7, 2024 07:50
Co-authored-by: PatrykKuniczak <64608510+PatrykKuniczak@users.noreply.github.com>
@Jonghakseo
Copy link
Owner Author

Jonghakseo commented Aug 6, 2024

I added example on README.md :)

252277e

@Jonghakseo
Copy link
Owner Author

@PatrykKuniczak I'm gonna merge this for now, and enahnce the documentation!

@Jonghakseo Jonghakseo merged commit fbe90fb into main Aug 17, 2024
4 checks passed
@Jonghakseo Jonghakseo deleted the feat/i18n branch August 17, 2024 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add I18n package
2 participants