Blocks are a key concept in the @feugene/fint-i18n architecture. They allow for logical separation of translations, isolation of application modules, and performance optimization.
A block is a named set of messages. For example: auth, admin, common, dashboard.
Advantages of using blocks:
- Isolation: Keys in different blocks do not conflict.
- Lazy Loading: You can load translations for a specific page only when the user navigates to it.
- Performance: The engine looks for keys within the target block first, which is faster than searching through a giant global object.
This is the most convenient way for Vue components. It automatically loads the specified blocks and manages their lifecycle (reference counting).
<script setup>
import { useI18nScope } from '@feugene/fint-i18n/vue'
// Asynchronously load blocks before the component renders
await useI18nScope(['auth', 'profile'])
</script>Tip
Use <Suspense> at the root of your application for asynchronous composables to work correctly.
The loadBlock method allows you to load a block at any time.
const { loadBlock } = useFintI18n()
await loadBlock('admin')
console.log('Block loaded!')Technically, yes, but in practice, you are always using at least one block.
If you want a flat structure "like in classic i18n," simply define all your keys in a single block, such as app.
// messages.ts
export const loaders = {
en: {
app: () => import('./locales/en.json')
}
}
// Usage
t('app.welcome')
t('app.buttons.save')The library requires that the first part of a key is always the block name. This ensures high performance during resolution (searching the Flat Map cache is O(1)).
You can create nested blocks by using a dot in the name.
// In messages.ts
const loaders = {
en: {
'pages.articles': () => import('./articles.json'),
},
}
// In code
await useI18nScope(['pages.articles'])
t('pages.articles.title')If you load pages.articles, the library marks this path as loaded. You can still have a separate loader for the parent block pages.
When loadBlock(blockName) is called, the library follows these rules:
- It first searches for an exact match for
blockName. - If no exact match is found and the block is nested, it looks for the nearest parent block.
- If multiple loaders are registered for the found block, they are executed sequentially.
Example:
const loaders = {
en: {
page: () => import('./page.json'),
},
}
await i18n.loadBlock('page.articles')In this case, the loader for the page block will be used.
createFintI18n() can accept multiple package collections:
const i18n = createFintI18n({
locale: 'en',
fallbackLocale: 'en',
loaders: [package1LocaleLoaders, package2LocaleLoaders],
})Merge rules:
- Package collections are merged from left to right.
- Identical block keys do not conflict; they are combined into a common list of loaders.
- If a loader within a package is already an array, it is flattened into this common list.
- In case of message key conflicts, the last loaded value wins.
useI18nScope implements a Reference Counting mechanism.
- When the first component requests the
authblock, it is loaded into memory. - When other components request
auth, they use the already loaded data. - (Optional) When the last component using
authis unmounted, the data can be cleared to save memory (if configured).